package org.batfish.datamodel;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.collect.ImmutableMap;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import javax.annotation.Nullable;

public final class TcpPort {

  /**
   * Well-known TCP ports with names that are useful to users. Does not need to be complete, but
   * ports must be unique.
   *
   * <p>Names are autogenerated by replacing _ with - in the name, e.g., "FTP-CONTROL".
   */
  private enum Values {
    BGP(179),
    DNS(53),
    ECHO(7),
    FTP_CONTROL(21),
    FTP_DATA(20),
    GOPHER(70),
    HTTP(80),
    HTTPS(443),
    IMAP(143),
    IMAP3(220),
    IMAPS(993),
    KSHELL(544),
    MYSQL(3306),
    POP2(109),
    POP3(110),
    POP3S(995),
    RDP(3389),
    RTSP(554),
    SMTP(25),
    SMTPS(465),
    SNMPTRAP(162),
    SSH(22),
    TELNET(23),
    ;

    private final int _number;

    Values(int number) {
      _number = number;
    }
  }

  /** ImmutableMap rejects duplicate keys, confirming that all numbers are distinct. */
  private static final Map<Integer, TcpPort> WELL_KNOWN_PORTS =
      Arrays.stream(Values.values())
          .map(v -> new SimpleImmutableEntry<>(v._number, v.name().toUpperCase().replace('_', '-')))
          .collect(
              ImmutableMap.toImmutableMap(
                  Entry::getKey, e -> new TcpPort(e.getKey(), e.getValue())));

  public static TcpPort of(int number) {
    checkArgument(1 <= number && number <= 65535, "Invalid TCP port number %s", number);
    return WELL_KNOWN_PORTS.getOrDefault(number, new TcpPort(number, null));
  }

  public int getNumber() {
    return _number;
  }

  public Optional<String> getName() {
    return Optional.ofNullable(_name);
  }

  // Private implementation below

  private TcpPort(int number, @Nullable String name) {
    _number = number;
    _name = name;
  }

  private final int _number;
  private final @Nullable String _name;
}
